package cc.android.testapp.xp.hooker.adblock.track;

import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.view.ViewGroup;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Objects;
import java.util.WeakHashMap;

import cc.android.testapp.util.CLog;
import cc.android.testapp.xp.hooker.adblock.core.AdRule;
import cc.commons.util.reflect.MethodUtil;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;

public class TrackHelper {

    private final static IdentityHashMap<Message, Handler> mMsgs = new IdentityHashMap<>();
    private static ActTrack mTrack = null;
    private static CallB mCallB = null;
    private static XC_MethodHook.Unhook MSG_HOOK = null;
    private static boolean mStop = false;

    public static final WeakHashMap<String, Object> fragments = new WeakHashMap<>();
    public static final WeakHashMap<String, Object> listeners = new WeakHashMap<>();

    public static void startTrack(Context pAct, AdRule pRule, CallB pCall) {
        CLog.log("直接跟踪开始");
        mTrack = new ActTrack(pAct, pRule);//点击后直接操作Activity或者Fragment
        mMsgs.clear();
        mCallB = pCall;
        // 获取点击操作后发送的延迟消息,用于可能的延迟操作检查
        MSG_HOOK = XposedHelpers.findAndHookMethod(Handler.class, "sendMessageDelayed"
                , Message.class, long.class, new XC_MethodHook() {
                    @Override
                    protected void afterHookedMethod(MethodHookParam pParam) {
                        if ((boolean) pParam.getResult() && !pParam.thisObject.getClass().getName().startsWith("android")) {
                            mMsgs.put((Message) pParam.args[0], (Handler) pParam.thisObject);
                            CLog.log("Msg拦截" + pParam.args[0]);
                        }
                    }
                });
        mTrack.enable = true;
    }

    public static void finishTrack() {
        mTrack.stopTrack();
        MSG_HOOK.unhook();
        CLog.log("直接跟踪结束");

        //点击后直接操作Activity或者Fragment能获取到结果
        if (mTrack.rule.isDirectSkip() || mMsgs.isEmpty()) {
            CLog.log("直接跟踪有结果,退出跟踪");
            mCallB.call(mTrack.rule);
            return;
        }
        CLog.log("Msg跟踪");

        // 延时拦截,规则应使用副本以防止编辑已经存入缓存的规则
        mTrack = new ActTrack(mTrack.act.get(), mTrack.rule.copy());
        long tOverTime = System.currentTimeMillis() + 3000;
        HashSet<Class<?>> tHandlerC = new HashSet<>();
        for (Handler sHandler : mMsgs.values()) {
            if (tHandlerC.contains(sHandler.getClass())) continue;

            Method tM = XposedHelpers.findMethodExactIfExists(sHandler.getClass(), "handleMessage", Message.class);
            if (tM == null) continue;

            tHandlerC.add(sHandler.getClass());
            XC_MethodHook.Unhook tUHook = XposedBridge.hookMethod(tM, new XC_MethodHook() {
                Message mNow = null;

                @Override
                protected void beforeHookedMethod(MethodHookParam pParam) {
                    Message tMsg = (Message) pParam.args[0];
                    Handler tHandler = mMsgs.remove(tMsg);
                    if (tHandler != null) {
                        //CLog.log("Msg拦截(2_start)" + pParam.args[0] + " method: " + pParam.method);
                        mNow = tMsg;
                        mTrack.enable = true;
                    }

                    if (mMsgs.isEmpty() || System.currentTimeMillis() > tOverTime) {
                        stopTrackMsg();
                    }
                }

                @Override
                protected void afterHookedMethod(MethodHookParam pParam) {
                    if (pParam.args[0] == mNow) {
                        //CLog.log("Msg拦截(2_end)" + pParam.args[0] + " method: " + pParam.method);
                        mTrack.enable = false;
                    }
                }
            });
            mTrack.hooks.add(tUHook);
        }
        new Handler(Looper.getMainLooper()).postDelayed(TrackHelper::stopTrackMsg, 5000);
    }

    private static void stopTrackMsg() {
        if (mStop) return;
        mStop = true;

        mMsgs.clear();
        mTrack.stopTrack();
        if (mTrack.rule.isDirectSkip()) mCallB.call(mTrack.rule);
        mTrack = null;
        CLog.log("Msg拦截结束或超时退出");
    }

    public interface CallB {
        void call(AdRule pRule);
    }

    public static Object getFragmentManager(Activity pAct) {
        if (pAct instanceof FragmentActivity) {
            return ((FragmentActivity) pAct).getSupportFragmentManager();
        } else return pAct.getFragmentManager();
    }

    public static Object beginTransaction(Activity pAct) {
        Object tMan = getFragmentManager(pAct);
        return MethodUtil.invokeDeclaredMethod(tMan.getClass(), "beginTransaction", tMan);
    }

    public static Object beginTransaction2(Object pMan) {
        return MethodUtil.invokeDeclaredMethod(pMan.getClass(), "beginTransaction", pMan);
    }

    public static ArrayList<View> getAllViews(Activity pAct) {
        ArrayList<View> tViews = new ArrayList<>();
//        if (pAct instanceof FragmentActivity) {
//            FragmentActivity tAct = (FragmentActivity) pAct;
//            tAct.getSupportFragmentManager().getFragments().forEach((fragment) -> {
//                CLog.log("Fragment: "+fragment.getClass().getName());
//                tViews.add(fragment.getView());
//            });
//
//        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//            pAct.getFragmentManager().getFragments().forEach((fragment) -> {
//                CLog.log("Fragment: "+fragment.getClass().getName());
//                tViews.add(fragment.getView());
//            });
//
//        }
        tViews.add(getViewGroup(pAct));

        tViews.removeIf(Objects::isNull);
        return tViews;
    }

    public static View findView(List<View> pViews, int pId) {
        for (View sView : pViews) {
            View tV = sView.findViewById(pId);
            if (tV != null) return tV;
        }
        return null;
    }

    public static ViewGroup getViewGroup(Activity activity) {
        return (ViewGroup) activity.getWindow().getDecorView().findViewById(android.R.id.content);
    }
}
